/*
 * acooly.cn Inc.
 * Copyright (c) 2016 All Rights Reserved.
 * create by zhangpu
 * date:2016年3月20日
 *
 */
package com.ouwen.smartpay.service.notify;

import com.alibaba.dubbo.config.annotation.Reference;
import com.fm.common.emuns.MessageType;
import com.fm.common.emuns.TaskExecuteStatus;
import com.fm.common.emuns.TaskStatus;
import com.fm.common.order.NotifyOrder;
import com.fm.core.utils.Ids;
import com.fm.core.utils.Strings;
import com.fm.core.utils.enums.ResultStatus;
import com.google.common.collect.Lists;
import com.ouwen.smartpay.entity.NotifyMessage;
import com.ouwen.smartpay.openapi.api.OpenApiNotifyFacade;
import com.ouwen.smartpay.openapi.order.NotifyOrderItem;
import com.ouwen.smartpay.openapi.order.OpenApiNotifyOrder;
import com.ouwen.smartpay.openapi.result.OpenApiNotifyResult;
import com.ouwen.smartpay.service.NotifyMessageSendHandler;
import com.ouwen.smartpay.service.NotifyMessageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;


/***
 *@author <a href="http://youngitman.tech">青年IT男</a>
 *@version v1.0.0
 *@className RPCNotifyMessageSendHandler
 *@description RPC消息发送处理器
 *@JunitTest: {@link  }
 *@date 16:13 2020-04-27
 *
 **/
@Component
public class RPCNotifyMessageSendHandler implements NotifyMessageSendHandler, InitializingBean {

    private static Logger logger = LoggerFactory.getLogger(RPCNotifyMessageSendHandler.class);

    /**
     * 这里由于openapi上增加一层所有不使用rpc(@Reference(version = "1.0.0"))，直接使用注入
     */
    @Autowired
    private OpenApiNotifyFacade openApiNotifyFacade;

    @Resource
    private NotifyMessageService notifyMessageService;

    @Resource
    private TaskExecutor notifyTaskExecutor;


    /****
     *
     * 线程池发送
     *
     * @author liyong
     * @date 14:21 2020-04-27
     * @param notifyMessage
     * @param order
     * @exception
     * @return void
     **/
    @Override
    public void send(NotifyMessage notifyMessage, NotifyOrder order) {

        notifyTaskExecutor.execute(() -> doSend(notifyMessage, order));

    }

    protected void doSend(final NotifyMessage notifyMessage, final NotifyOrder order) {

        MDC.put("gid", notifyMessage.getGid());

        Date now = new Date();

        // 如果非第一次通知，下次通知时间还没有到，则忽略
        if (notifyMessage.getSendCount() != 0 && notifyMessage.getNextSendTime() != null && notifyMessage.getNextSendTime().after(now)) {

            return;
        }

        // 处理前锁定
        int rows = notifyMessageService.updateProccessingStatus(notifyMessage.getId());

        if (rows <= 0) {
            //被其他节点或线程处理了,放弃发送
            return;
        }

        notifyMessage.setSendCount(notifyMessage.getSendCount() + 1);

        logger.info("第{}次通知", notifyMessage.getSendCount());

        OpenApiNotifyResult openApiNotifyResult;

        String respInfo;

        try {

            logger.info("通知地址:{}", notifyMessage.getNotifyUrl());

            logger.info("通知内容:{}", order.toString());

            openApiNotifyResult = notifyOtherService(order, notifyMessage);

            logger.info("通知结果:{}", openApiNotifyResult);

            respInfo = openApiNotifyResult.getStatus().code();

        } catch (Exception e) {

            openApiNotifyResult = new OpenApiNotifyResult();

            openApiNotifyResult.setStatus(ResultStatus.failure);

            openApiNotifyResult.setCode(ResultStatus.failure.code());

            respInfo = e.getMessage();

            logger.error("第{}次通知 失败，原因:{}", notifyMessage.getSendCount(), e.getMessage());
        }

        TaskStatus taskStatus;


        if (openApiNotifyResult != null && ResultStatus.success == openApiNotifyResult.getStatus()) {

            logger.info("第{}次通知成功", notifyMessage.getSendCount());

            taskStatus = TaskStatus.Success;

        } else {

            logger.info("第{}次通知失败", notifyMessage.getSendCount());

            int sendCount = notifyMessage.getSendCount();

            if (sendCount >= NotifyConfig.notifyCount) {

                taskStatus = TaskStatus.Finish;

                logger.info("通知失败次数达到阀值，结束通知任务");

            } else {

                taskStatus = TaskStatus.Waitting;

                notifyMessage.setNextSendTime(getNextNotifyTime(sendCount - 1));

                logger.info("通知失败次数未达到阀值，等待下次通知");

            }
        }

        notifyMessage.setStatus(taskStatus);

        notifyMessage.setExecuteStatus(TaskExecuteStatus.Unprocessed);

        if (Strings.length(respInfo) > 128) {

            respInfo = Strings.substring(respInfo, 0, 128);

        }
        notifyMessage.setRespInfo(respInfo);

        logger.info("通知消息保存: notifyMessage:{}", notifyMessage);

        notifyMessageService.update(notifyMessage);

        MDC.clear();
    }


    /***
     *
     * 通知其他接入服务
     *
     * @author liyong
     * @date 11:45 2020-04-27
     * @exception
     * @return void
     **/
    private OpenApiNotifyResult notifyOtherService(NotifyOrder order, NotifyMessage notifyMessage) {

        OpenApiNotifyOrder openApiNotifyOrder = new OpenApiNotifyOrder();

        openApiNotifyOrder.setRequestNo(Ids.oid());

        openApiNotifyOrder.setTotal(1);

        openApiNotifyOrder.setGid(notifyMessage.getGid());

        openApiNotifyOrder.setPartnerId(notifyMessage.getPartnerId());

        List<NotifyOrderItem> notifyOrderItems = Lists.newArrayList();

        NotifyOrderItem notifyOrderItem = new NotifyOrderItem();

        notifyOrderItem.setRequestNo(notifyMessage.getRequestNo());

        notifyOrderItem.setMerchOrderNo(notifyMessage.getMerchOrderNo());

        notifyOrderItem.setRespCode(order.getRespCode());

        notifyOrderItem.setRespMsg(order.getRespMsg());

        notifyOrderItem.setTranDate(order.getTranDate());

        notifyOrderItem.setTranStatus(order.getTranStatus());

        notifyOrderItem.setTranDesc(order.getTranDesc());

        notifyOrderItem.setDescription(order.getDescription());

        notifyOrderItems.add(notifyOrderItem);

        openApiNotifyOrder.setNotifyOrderItems(notifyOrderItems);

        //发起异步通知调用
        OpenApiNotifyResult result = openApiNotifyFacade.notify(openApiNotifyOrder);

        //需要重新通知，更新异步通知消息结果
        if (result == null || ResultStatus.success != result.getStatus()) {

            logger.info("第{}次通知失败", notifyMessage.getSendCount());

            notifyMessage.setSendCount(notifyMessage.getSendCount() + 1);

            int sendCount = notifyMessage.getSendCount();

            TaskStatus taskStatus;

            if (sendCount >= NotifyConfig.notifyCount) {

                taskStatus = TaskStatus.Finish;

                logger.info("通知失败次数达到阀值，结束通知任务");

            } else {

                taskStatus = TaskStatus.Waitting;

                notifyMessage.setNextSendTime(getNextNotifyTime(sendCount - 1));

                logger.info("通知失败次数未达到阀值，等待下次通知");

            }
            notifyMessage.setSendCount(notifyMessage.getSendCount() + 1);

            //发送失败需要重新发送
            notifyMessage.setStatus(taskStatus);

            notifyMessage.setExecuteStatus(TaskExecuteStatus.Unprocessed);

            notifyMessage.setNextSendTime(getNextNotifyTime(notifyMessage.getSendCount()));

        }

        return result;
    }

    /***
     *
     * 获取下次执行时间
     *
     * @author liyong
     * @date 14:52 2020-04-27
     * @param num
     * @exception
     * @return java.util.Date
     **/
    private static Date getNextNotifyTime(int num) {

        GregorianCalendar calendar = new GregorianCalendar();

        calendar.setTime(new Date());

        calendar.add(Calendar.MINUTE, NotifyConfig.notifyTime[num]);

        return calendar.getTime();
    }


    @Override
    public MessageType getNotifyMessageType() {
        return MessageType.RPC;
    }

    @Override
    public void afterPropertiesSet() {

    }
}
